/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.client;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;

/**
 * Factory of all commands types. In charge of building and also executing commands, though scheduler.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 09 December 2010
 */
public final class OPCommandFactory {

    // different types of failure. Not put in an enum because it is put in a field of the javascript object and should be transfered between module.
    /**
     * Load a part. Asynchronous, except if the part is already loaded. Can fail with a {@link OPCommandFailureReason#TYPE_LOAD_TIMEOUT} failure if the load time
     * out.
     */
    public static final int LOAD_PART = 1;

    /**
     * Display a view in the given slot id. Dependent of the service, if the service is not already loaded it will be loaded first. Can fail with a
     * {@link OPCommandFailureReason#TYPE_NO_SUCH_VIEW} failure if the view is not binded, with a {@link OPCommandFailureReason#TYPE_MISSING_MANDATORY_PARAMS}
     * failure if the view needs mandatory params and those are not given in the call, with a {@link OPCommandFailureReason#TYPE_PRE_COMMAND_FAILED} failure if
     * the command needs a pre loading command and this pre command fails, or with a {@link OPCommandFailureReason#TYPE_NO_SUCH_SLOT} failure if there is no
     * slot with the given slot id on the page to attach a view on it.
     */
    public static final int DISPLAY_VIEW = 2;

    /**
     * Dispatch an event on the even bus. No dependence, no asynchronous. Cannot fail.
     */
    public static final int DISPATCH_EVENT = 3;

    /**
     * Dispatch an event to a specific service only. Dependent of the service, if the service is not already loaded it will be loaded first. Can fail with a
     * {@link OPCommandFailureReason#TYPE_NO_SUCH_SERVICE} failure if the service is not binded, or with a {@link OPCommandFailureReason#TYPE_PRE_COMMAND_FAILED}
     * failure if the command needs a pre loading command and this pre command fails.
     */
    public static final int DISPATCH_EVENT_TO = 4;

    /**
     * Dispatch a message to an embedded view. No dependence, no asynchronous. Can fail with a {@link OPCommandFailureReason#TYPE_NO_SUCH_ID_IN_THE_PAGE}
     * failure if there is no element on the page with the given slotId, with a {@link OPCommandFailureReason#TYPE_GIVEN_ID_IS_NOT_A_SLOT} failure if the given
     * slotId is not attached to an {@link OPSlot} slot, or with a {@link OPCommandFailureReason#TYPE_NO_VIEW_ATTACHED_TO_PROXY} failure if there is no view
     * attached to the proxy that called this method.
     */
    public static final int DISPATCH_MESSAGE_TO_EMBEDDED_VIEW = 5;

    /**
     * Dispatch a message to a container slot. No dependence, no asynchronous. Can fail with a {@link OPCommandFailureReason#TYPE_NO_SUCH_ID_IN_THE_PAGE}
     * failure if there is no element on the page with the given slotId, with a {@link OPCommandFailureReason#TYPE_GIVEN_ID_IS_NOT_A_SLOT} failure if the given
     * slotId is not attached to an {@link OPSlot} slot, or with a {@link OPCommandFailureReason#TYPE_NO_SLOT_ATTACHED_TO_PROXY} failure if there is no slot
     * attached to the proxy that called this method.
     */
    public static final int DISPATCH_MESSAGE_TO_CONTAINER_SLOT = 6;

    /**
     * Name used in params to save the resource name.
     */
    public static final String PARAM_RESOURCE = "op_command_param_resource";

    /**
     * Name used in params to save the action name.
     */
    public static final String PARAM_ACTION = "op_command_param_action";

    /**
     * Name used in params to save the slot id.
     */
    public static final String PARAM_SLOT_ID = "op_command_param_slot_id";

    /**
     * Name used in params to save the event.
     */
    public static final String PARAM_EVENT = "op_command_param_event";

    /**
     * Name used in params to save the message.
     */
    public static final String PARAM_MESSAGE = "op_command_param_message";

    /**
     * Constructor for a command to load a part.
     * <p>
     * The command is asynchronous, except if the part is already loaded.
     * <p>
     * Can fail with a {@link OPCommandFailureReason#TYPE_LOAD_TIMEOUT} failure if the load time out.
     * 
     * @param service
     *            the service name of the part to load. Shouldn't be null.
     * @param callback
     *            the callback to call when the command is finished. Can be null.
     * @return the command.
     */
    public static OPCommand loadPartCommandFactory(final String service, final OPCommandCallback callback) {
        assert service != null : "parameter service shouldn't be null";

        return OPCommand.commandFactory(LOAD_PART, service, null, callback);
    }

    /**
     * Constructor for a command to display a view. The parameters given to the command shouldn't contains params associated with the keys
     * {@link #PARAM_RESOURCE}, {@link OPCommandFactory#PARAM_ACTION} or {@link #PARAM_SLOT_ID}. If it is the case, those parameters will be overriden.
     * <p>
     * The command is dependent of the service, if the service is not already loaded it will be loaded first.
     * <p>
     * Can fail with a {@link OPCommandFailureReason#TYPE_NO_SUCH_VIEW} failure if the view is not binded, with a
     * {@link OPCommandFailureReason#TYPE_MISSING_MANDATORY_PARAMS} failure if the view needs mandatory params and those are not given in the call, with a
     * {@link OPCommandFailureReason#TYPE_PRE_COMMAND_FAILED} failure if the command needs a pre loading command and this pre command fails, or with a
     * {@link OPCommandFailureReason#TYPE_NO_SUCH_SLOT} failure if the given slot id is not attached to a element on the page.</dd>
     * 
     * @param service
     *            the service name of the part containing the view. Shouldn't be null.
     * @param resource
     *            the resource name for the displayed view. Shouldn't be null.
     * @param action
     *            the action name for the displayed view. Shouldn't be null.
     * @param params
     *            parameters of the command. Can be null.
     * @param slotId
     *            the id of the slot where to display the view. Shouldn't be null. Shouldn't be empty.
     * @return the command.
     */
    public static OPCommand displayViewCommandFactory(final String service, final String resource, final String action, final OPParams params,
            final String slotId) {
        assert service != null : "parameter service shouldn't be null";
        assert resource != null : "parameter resource shouldn't be null";
        assert action != null : "parameter action shouldn't be null";
        assert slotId != null : "parameter slotId shouldn't be null";
        assert !slotId.isEmpty() : "parameter slotId shouldn't be empty";

        // add resource, action and slotId as parameters
        final OPParams finalParams = new OPParams(PARAM_RESOURCE, resource, PARAM_ACTION, action, PARAM_SLOT_ID, slotId);

        // add existing params to the parameters
        if (params != null) {
            final String[] existingParamsNames = params.getNames();
            for (int i = 0; i < existingParamsNames.length; i++) {
                String existingParamsName = existingParamsNames[i];

                assert !existingParamsName.equals(PARAM_RESOURCE) : "params shouldn't contain a " + PARAM_RESOURCE + " param";
                assert !existingParamsName.equals(PARAM_ACTION) : "params shouldn't contain an " + PARAM_ACTION + " param";
                assert !existingParamsName.equals(PARAM_SLOT_ID) : "params shouldn't contain a " + PARAM_SLOT_ID + " param";
                if ((PARAM_RESOURCE.equals(existingParamsName)) || (PARAM_ACTION.equals(existingParamsName)) || (PARAM_SLOT_ID.equals(existingParamsName))) {
                    continue;
                }

                finalParams.addParam(existingParamsName, params.getParamValue(existingParamsName));
            }
        }

        return OPCommand.commandFactory(DISPLAY_VIEW, service, finalParams, null);
    }

    /**
     * Constructor for a command to dispatch an event on the event bus. The parameters given to the command shouldn't contains param associated with the key
     * {@link #PARAM_EVENT}. If it is the case, this parameter will be overriden.
     * <p>
     * No dependence, no asynchronous. Cannot fail.
     * 
     * @param event
     *            the event to dispatch. Shouldn't be null.
     * @param params
     *            the parameters associated with the event. Can be null.
     * @return the command.
     */
    public static OPCommand dispatchEventCommandFactory(final String event, final OPParams params) {
        assert event != null : "parameter event shouldn't be null";

        // add event as parameter
        final OPParams finalParams = new OPParams(PARAM_EVENT, event);

        // add existing params to the parameters
        if (params != null) {
            final String[] existingParamsNames = params.getNames();
            for (int i = 0; i < existingParamsNames.length; i++) {
                String existingParamsName = existingParamsNames[i];

                assert !existingParamsName.equals(PARAM_EVENT) : "params shouldn't contain an " + PARAM_EVENT + " param";
                if (PARAM_EVENT.equals(existingParamsName)) {
                    continue;
                }

                finalParams.addParam(existingParamsName, params.getParamValue(existingParamsName));
            }
        }

        return OPCommand.commandFactory(DISPATCH_EVENT, null, finalParams, null);
    }

    /**
     * Constructor for a command to dispatch an event to a specific service. The parameters given to the command shouldn't contains param associated with the
     * key {@link #PARAM_EVENT}. If it is the case, this parameter will be overriden.
     * <p>
     * The command is dependent of the service, if the service is not already loaded it will be loaded first.
     * <p>
     * Can fail with a {@link OPCommandFailureReason#TYPE_NO_SUCH_SERVICE} failure if the service is not binded, or with a
     * {@link OPCommandFailureReason#TYPE_PRE_COMMAND_FAILED} failure if the command needs a pre loading command and this pre command fails.
     * 
     * @param service
     *            the service to which dispatch the event. Shouldn't be null.
     * @param event
     *            the event to dispatch. Shouldn't be null.
     * @param params
     *            the parameters associated with the event. Can be null.
     * @return the command.
     */
    public static OPCommand dispatchEventToCommandFactory(final String service, final String event, final OPParams params) {
        assert service != null : "parameter service shouldn't be null";
        assert event != null : "parameter event shouldn't be null";

        // add event as parameter
        final OPParams finalParams = new OPParams(PARAM_EVENT, event);

        // add existing params to the parameters
        if (params != null) {
            final String[] existingParamsNames = params.getNames();
            for (int i = 0; i < existingParamsNames.length; i++) {
                String existingParamsName = existingParamsNames[i];

                assert !existingParamsName.equals(PARAM_EVENT) : "params shouldn't contain an " + PARAM_EVENT + " param";
                if (PARAM_EVENT.equals(existingParamsName)) {
                    continue;
                }

                finalParams.addParam(existingParamsName, params.getParamValue(existingParamsName));
            }
        }

        return OPCommand.commandFactory(DISPATCH_EVENT_TO, service, finalParams, null);
    }

    /**
     * Constructor for a command to dispatch a message to an embedded view. The parameters given to the command shouldn't contains param associated with the
     * keys {@link #PARAM_MESSAGE} and {@link #PARAM_SLOT_ID}. If it is the case, this parameter will be overriden.
     * <p>
     * No dependence, no asynchronous.
     * <p>
     * Can fail with a {@link OPCommandFailureReason#TYPE_NO_SUCH_ID_IN_THE_PAGE} failure if there is no element on the page with the given slotId, with a
     * {@link OPCommandFailureReason#TYPE_GIVEN_ID_IS_NOT_A_SLOT} failure if the given slotId is not attached to an {@link OPSlot} slot, or with a
     * {@link OPCommandFailureReason#TYPE_NO_VIEW_ATTACHED_TO_PROXY} failure if there is no view attached to the proxy that called this method.
     * 
     * @param message
     *            the message to dispatch. Shouldn't be null.
     * @param slotId
     *            the slot id of the view to which the message should be dispatched. Shouldn't be null. Shouldn't be empty.
     * @param params
     *            the parameters associated with the message. Can be null.
     * @return the command.
     */
    public static OPCommand dispatchMessageToEmbeddedViewFactory(final String message, final String slotId, final OPParams params) {
        assert message != null : "parameter message shouldn't be null";
        assert slotId != null : "parameter slotId shouldn't be null";
        assert !slotId.isEmpty() : "parameter slotId shouldn't be empty";

        // add message and slotId as parameters
        final OPParams finalParams = new OPParams(PARAM_MESSAGE, message, PARAM_SLOT_ID, slotId);

        // add existing params to the parameters
        if (params != null) {
            final String[] existingParamsNames = params.getNames();
            for (int i = 0; i < existingParamsNames.length; i++) {
                String existingParamsName = existingParamsNames[i];

                assert !existingParamsName.equals(PARAM_MESSAGE) : "params shouldn't contain a " + PARAM_MESSAGE + " param";
                assert !existingParamsName.equals(PARAM_SLOT_ID) : "params shouldn't contain a " + PARAM_SLOT_ID + " param";
                if ((PARAM_MESSAGE.equals(existingParamsName)) || (PARAM_SLOT_ID.equals(existingParamsName))) {
                    continue;
                }

                finalParams.addParam(existingParamsName, params.getParamValue(existingParamsName));
            }
        }

        return OPCommand.commandFactory(DISPATCH_MESSAGE_TO_EMBEDDED_VIEW, null, finalParams, null);
    }

    /**
     * Constructor for a command to dispatch a message to container slot. The parameters given to the command shouldn't contains param associated with the keys
     * {@link #PARAM_MESSAGE} and {@link #PARAM_SLOT_ID}. If it is the case, this parameter will be overriden.
     * <p>
     * No dependence, no asynchronous.
     * 
     * Can fail with a {@link OPCommandFailureReason#TYPE_NO_SUCH_ID_IN_THE_PAGE} failure if there is no element on the page with the given slotId, with a
     * {@link OPCommandFailureReason#TYPE_GIVEN_ID_IS_NOT_A_SLOT} failure if the given slotId is not attached to an {@link OPSlot} slot, or with a
     * {@link OPCommandFailureReason#TYPE_NO_SLOT_ATTACHED_TO_PROXY} failure if there is no slot attached to the proxy that called this method.
     * 
     * @param message
     *            the message to dispatch. Shouldn't be null.
     * @param slotId
     *            the slot id of the slot to which the message should be dispatched. Shouldn't be null. Shouldn't be empty.
     * @param params
     *            the parameters associated with the message. Can be null.
     * @return the command.
     */
    public static OPCommand dispatchMessageToContainerSlotFactory(final String message, final String slotId, final OPParams params) {
        assert message != null : "parameter message shouldn't be null";
        assert slotId != null : "parameter slotId shouldn't be null";
        assert !slotId.isEmpty() : "parameter slotId shouldn't be empty";

        // add message and slotId as parameters
        final OPParams finalParams = new OPParams(PARAM_MESSAGE, message, PARAM_SLOT_ID, slotId);

        // add existing params to the parameters
        if (params != null) {
            final String[] existingParamsNames = params.getNames();
            for (int i = 0; i < existingParamsNames.length; i++) {
                String existingParamsName = existingParamsNames[i];

                assert !existingParamsName.equals(PARAM_MESSAGE) : "params shouldn't contain a " + PARAM_MESSAGE + " param";
                assert !existingParamsName.equals(PARAM_SLOT_ID) : "params shouldn't contain a " + PARAM_SLOT_ID + " param";
                if ((PARAM_MESSAGE.equals(existingParamsName)) || (PARAM_SLOT_ID.equals(existingParamsName))) {
                    continue;
                }

                finalParams.addParam(existingParamsName, params.getParamValue(existingParamsName));
            }
        }

        return OPCommand.commandFactory(DISPATCH_MESSAGE_TO_CONTAINER_SLOT, null, finalParams, null);
    }

    /**
     * Returns the pre-command of which the given command is dependent, or null if there is none. For example, a command that depends on a service needs to have
     * the service loaded before it can be executed.
     * 
     * @param command
     *            the given command. Shouldn't be null.
     * @return the pre-command of which the given command is dependent, or null if there is none.
     */
    public static OPCommand getPreCommand(final OPCommand command) {
        assert command != null : "parameter command shouldn't be null";

        OPCommand preCommand = null;

        switch (command.getType()) {
        case LOAD_PART:
        case DISPATCH_MESSAGE_TO_EMBEDDED_VIEW:
        case DISPATCH_MESSAGE_TO_CONTAINER_SLOT:
            // load part command and dispatch message commands dont depend on any other commands
            break;
        case DISPATCH_EVENT:
            // dispatch even to command doesn't necessary depends on a service, but can if the user specify one in the command
            final String service = command.getService();
            if (service != null) {
                // user specified a service, so make the command dependent of the service.
                preCommand = loadPartPreCommand(service);
            }
            break;
        case DISPLAY_VIEW:
        case DISPATCH_EVENT_TO:
            // display view command and dispatch event to command are dependent of a service
            assert command.getService() != null : "command service shouldn't be null";
            preCommand = loadPartPreCommand(command.getService());
            break;
        default:
            // oops, don't know the type of the command. If we don't know, then don't do anything
            break;
        }

        return preCommand;
    }

    /**
     * Create and return a load part command to be used as a pre command, or null if the service is already loaded.
     * 
     * @param service
     *            the service name of the part to load. Shouldn't be null.
     * @return the pre-command. Can be null.
     */
    private static OPCommand loadPartPreCommand(final String service) {
        if (OPShell.isPartAlreadyLoaded(service)) {
            return null;
        }

        return OPCommandFactory.loadPartCommandFactory(service, null);
    }

    /**
     * Execute the given command. Should not be called directly, it is called by {@link OPScheduler}. Call the callback with a {@link OPCommandFailureReason#TYPE_UNKNOW_COMMAND} failure if the
     * command is of an unknown type.
     * 
     * @param command
     *            the command to execute. Shouldn't be null.
     * @param scheduler
     *            the scheduler that asked for this command to execute. Can be null.
     * @return true if the command finished executing, false if the command is asynchronous.
     */
    public static boolean execute(final OPCommand command, final OPScheduler scheduler) {
        assert command != null : "command shouldn't be null";

        boolean result = true;
        switch (command.getType()) {
        case LOAD_PART:
            result = executeLoadPartCommand(command, scheduler);
            break;
        case DISPLAY_VIEW:
            result = executeDisplayViewCommand(command);
            break;
        case DISPATCH_EVENT:
            result = executeDispatchEventCommand(command);
            break;
        case DISPATCH_EVENT_TO:
            result = executeDispatchEventToCommand(command);
            break;
        case DISPATCH_MESSAGE_TO_EMBEDDED_VIEW:
            result = executeDispatchMessageToEmbeddedView(command);
            break;
        case DISPATCH_MESSAGE_TO_CONTAINER_SLOT:
            result = executeDispatchMessageToContainerSlot(command);
            break;
        default:
            // unknown command, call its onFail callback.
            command.onFail(OPCommandFailureReason.unknownCommandFailureFactory(command.getType()));
            break;
        }

        return result;
    }

    /**
     * Execute a load part command.
     * 
     * @param command
     *            the load command to execute. Shouldn't be null.
     * @param scheduler
     *            the scheduler that asked for this command to execute. Can be null
     * @return false if this command is executed asynchronously, true otherwise (service already loaded).
     */
    private static boolean executeLoadPartCommand(final OPCommand command, final OPScheduler scheduler) {
        // launch a part loading request, and register the callback to call us back
        assert command.getService() != null : "command service shouldn't be null";
        OPPartLoadingRequest loadingRequest = OPPartLoadingRequest.partLoadingRequestFactory(command.getService(), new OPPartLoadedCallback() {
            @Override
            public void onSuccess() {
                onSuccessLoadingPart(command, scheduler);
            }

            @Override
            public void onTimeout() {
                onFailLoadingPart(command, scheduler);
            }
        });
        OPShell.opBindingSharedInstance().executeLoadingRequest(loadingRequest);

        // check if the request has been executed synchronously
        if (loadingRequest.wasExecutedSynchronously()) {
            // yes, service was already loaded
            return true;
        }

        // no, so now scheduler have to wait for the part either to load or to fail loading
        return false;
    }

    /**
     * Execute a display view command. If there is no view binded for such a service, resource, and action name, the onFail callback of the command will be
     * called..
     * 
     * @param command
     *            the display view command to execute. Shouldn't be null.
     * @return true, as this command is executed synchronously.
     */
    private static boolean executeDisplayViewCommand(final OPCommand command) {
        // get service name
        final String service = command.getService();
        assert service != null : "command service shouldn't be null";

        // get parameters
        final OPParams params = command.getParams();
        assert params != null : "command params shouldn't be null";

        // get resource name
        final String resource = params.getParamValue(PARAM_RESOURCE);
        assert resource != null : "command resource shouldn't be null";

        // get action name
        final String action = params.getParamValue(PARAM_ACTION);
        assert action != null : "command action shouldn't be null";

        // get slotId
        final String slotId = params.getParamValue(PARAM_SLOT_ID);
        assert slotId != null : "command slotId shouldn't be null";

        // get the corresponding action
        final OPBindingAction bindedAction = OPShell.opBindingSharedInstance().getAction(service, resource, action);
        if (bindedAction == null) {
            // no way to display the view, it is not binded
            command.onFail(OPCommandFailureReason.noSuchViewFailureFactory(service, resource, action));
            return true;
        }

        // check that the slot id is really attached to an element on the page
        // do the check only if the slot id is not OPShell.SLOT_ID_ROOT
        if (!OPShell.SLOT_ID_ROOT.equals(slotId)) {
            final Element slot = DOM.getElementById(slotId);
            if (slot == null) {
                // no way to display the view, there is no real slot on the page
                command.onFail(OPCommandFailureReason.noSuchSlotFailureFactory(slotId));
                return true;
            }
        }

        // rebuild params without resource, action and slot id
        OPParams newParams = null;
        final String[] paramsNames = params.getNames();
        for (int i = 0; i < paramsNames.length; i++) {
            String paramName = paramsNames[i];
            if ((PARAM_RESOURCE.equals(paramName)) || (PARAM_ACTION.equals(paramName)) || (PARAM_SLOT_ID.equals(paramName))) {
                continue;
            }
            if (newParams == null) {
                newParams = new OPParams();
            }
            newParams.addParam(paramName, params.getParamValue(paramName));
        }

        // build the params JS
        OPParamsJS paramsJS = null;
        if (newParams != null) {
            paramsJS = OPParamsJS.buildFromOPParams(newParams);
        }

        // call the display view method on the action
        final String missingParam = bindedAction.displayView(slotId, paramsJS);

        // check if there was a missing params, and call the corresponding callback
        if (missingParam == null) {
            command.onSuccess();
        } else {
            command.onFail(OPCommandFailureReason.missingMandatoryParamFailureFactory(missingParam));
        }

        // it is a synchronous method, the view is displayed here
        return true;
    }

    /**
     * Execute a dispatch event command.
     * 
     * @param command
     *            the dispatch event command to execute. Shouldn't be null.
     * @return true, as this command is executed synchronously.
     */
    private static boolean executeDispatchEventCommand(final OPCommand command) {
        // get parameters
        final OPParams params = command.getParams();
        assert params != null : "command params shouldn't be null";

        // get event
        final String event = params.getParamValue(PARAM_EVENT);
        assert event != null : "command event shouldn't be null";

        // rebuild params without event
        OPParams newParams = null;
        final String[] paramsNames = params.getNames();
        for (int i = 0; i < paramsNames.length; i++) {
            String paramName = paramsNames[i];
            if (PARAM_EVENT.equals(paramName)) {
                continue;
            }
            if (newParams == null) {
                newParams = new OPParams();
            }
            newParams.addParam(paramName, params.getParamValue(paramName));
        }

        // build the params JS
        OPParamsJS paramsJS = null;
        if (newParams != null) {
            paramsJS = OPParamsJS.buildFromOPParams(newParams);
        }

        // call the real dispatch even method
        OPShell.opBindingSharedInstance().dispatchEvent(event, paramsJS);

        // call the success callback
        command.onSuccess();

        // it is a synchronous method, the view is displayed here
        return true;
    }

    /**
     * Execute a dispatch event to command.
     * 
     * @param command
     *            the dispatch event to command to execute. Shouldn't be null.
     * @return true, as this command is executed synchronously.
     */
    private static boolean executeDispatchEventToCommand(final OPCommand command) {
        // get service name
        final String service = command.getService();
        assert service != null : "command service shouldn't be null";

        // get parameters
        final OPParams params = command.getParams();
        assert params != null : "command params shouldn't be null";

        // get event
        final String event = params.getParamValue(PARAM_EVENT);
        assert event != null : "command event shouldn't be null";

        // rebuild params without event
        OPParams newParams = null;
        final String[] paramsNames = params.getNames();
        for (int i = 0; i < paramsNames.length; i++) {
            String paramName = paramsNames[i];
            if (PARAM_EVENT.equals(paramName)) {
                continue;
            }
            if (newParams == null) {
                newParams = new OPParams();
            }
            newParams.addParam(paramName, params.getParamValue(paramName));
        }

        // build the params JS
        OPParamsJS paramsJS = null;
        if (newParams != null) {
            paramsJS = OPParamsJS.buildFromOPParams(newParams);
        }

        // call the real dispatch event to method.
        final boolean commandExecutedOk = OPShell.opBindingSharedInstance().dispatchEventTo(service, event, paramsJS);

        // does the command execute correctly? Call the corresponding command callback.
        if (commandExecutedOk) {
            command.onSuccess();
        } else {
            command.onFail(OPCommandFailureReason.noSuchServiceFailureFactory(service));
        }

        // it is a synchronous method, the view is displayed here
        return true;
    }

    /**
     * Execute a dispatch message to embedded view command.
     * 
     * @param command
     *            the dispatch message to embedded view command to execute. Shouldn't be null.
     * @return true, as this command is executed synchronously.
     */
    private static boolean executeDispatchMessageToEmbeddedView(final OPCommand command) {
        // get parameters
        final OPParams params = command.getParams();
        assert params != null : "command params shouldn't be null";

        // get message
        final String message = params.getParamValue(PARAM_MESSAGE);
        assert message != null : "command message shouldn't be null";

        // get slot id
        final String slotId = params.getParamValue(PARAM_SLOT_ID);
        assert slotId != null : "command slot id shouldn't be null";

        // rebuild params without message and slot id
        OPParams newParams = null;
        final String[] paramsNames = params.getNames();
        for (int i = 0; i < paramsNames.length; i++) {
            String paramName = paramsNames[i];
            if ((PARAM_MESSAGE.equals(paramName)) || (PARAM_SLOT_ID.equals(paramName))) {
                continue;
            }
            if (newParams == null) {
                newParams = new OPParams();
            }
            newParams.addParam(paramName, params.getParamValue(paramName));
        }

        // build the params JS
        OPParamsJS paramsJS = null;
        if (newParams != null) {
            paramsJS = OPParamsJS.buildFromOPParams(newParams);
        }

        // find the dispatcher attached to this id
        final OPMessagesDispatcher dispatcher = OPMessagesDispatcher.getDispatcherAttachedToId(slotId);
        if (dispatcher == null) {
            // check the reason why dispatcher is null
            final Element slot = DOM.getElementById(slotId);
            if (slot == null) {
                // the slot id is not attached to a real element on the page
                command.onFail(OPCommandFailureReason.noSuchIdOnThePageFailureFactory(slotId));
            } else {
                // there is just no dispatcher attached to this slot id element, this means that the slot id is not attached to a slot
                command.onFail(OPCommandFailureReason.givenIdIsNotASlotFailureFactory(slotId));
            }
            // whatever, no way to dispatch the message
            return true;
        }

        // check that there is indeed a view attached to the dispatcher
        if (!dispatcher.isMessageFromContainerSlotHandlerCallbackRegistered()) {
            // no real view attached
            command.onFail(OPCommandFailureReason.noViewAttachedToProxyFailureFactory());
            return true;
        }

        // real dispatching of the message
        dispatcher.dispatchMessageToEmbeddedView(message, paramsJS);

        // call the success callback
        command.onSuccess();

        // it is a synchronous method, the message has been dispatched here
        return true;
    }

    /**
     * Execute a dispatch message to container slot command.
     * 
     * @param command
     *            the dispatch message to container slot command to execute. Shouldn't be null.
     * @return true, as this command is executed synchronously.
     */
    private static boolean executeDispatchMessageToContainerSlot(final OPCommand command) {
        // get parameters
        final OPParams params = command.getParams();
        assert params != null : "command params shouldn't be null";

        // get message
        final String message = params.getParamValue(PARAM_MESSAGE);
        assert message != null : "command message shouldn't be null";

        // get slot id
        final String slotId = params.getParamValue(PARAM_SLOT_ID);
        assert slotId != null : "command slot id shouldn't be null";

        // rebuild params without message and slot id
        OPParams newParams = null;
        final String[] paramsNames = params.getNames();
        for (int i = 0; i < paramsNames.length; i++) {
            String paramName = paramsNames[i];
            if ((PARAM_MESSAGE.equals(paramName)) || (PARAM_SLOT_ID.equals(paramName))) {
                continue;
            }
            if (newParams == null) {
                newParams = new OPParams();
            }
            newParams.addParam(paramName, params.getParamValue(paramName));
        }

        // build the params JS
        OPParamsJS paramsJS = null;
        if (newParams != null) {
            paramsJS = OPParamsJS.buildFromOPParams(newParams);
        }

        // find the dispatcher attached to this id
        final OPMessagesDispatcher dispatcher = OPMessagesDispatcher.getDispatcherAttachedToId(slotId);
        if (dispatcher == null) {
            // check the reason why dispatcher is null
            final Element slot = DOM.getElementById(slotId);
            if (slot == null) {
                // the slot id is not attached to a real element on the page
                command.onFail(OPCommandFailureReason.noSuchIdOnThePageFailureFactory(slotId));
            } else {
                // there is just no dispatcher attached to this slot id element, this means that the slot id is not attached to a slot
                command.onFail(OPCommandFailureReason.givenIdIsNotASlotFailureFactory(slotId));
            }
            // whatever, no way to dispatch the message
            return true;
        }

        // check that there is indeed a slot attached to the dispatcher
        if (!dispatcher.isMessageFromEmbeddedViewHandlerCallbackRegistered()) {
            // no real slot attached
            command.onFail(OPCommandFailureReason.noSlotAttachedToProxyFailureFactory());
            return true;
        }

        // real dispatching of the message
        dispatcher.dispatchMessageToContainerSlot(message, paramsJS);

        // call the success callback
        command.onSuccess();

        // it is a synchronous method, the message has been dispatched here
        return true;
    }

    /**
     * Called back when a load part command finished successfully. Call back the command execution loop, so that it can start again.
     * 
     * @param command
     *            the load part command that was successful. Shouldn't be null.
     * @param scheduler
     *            the scheduler that asked for this command to execute
     */
    public static void onSuccessLoadingPart(final OPCommand command, final OPScheduler scheduler) {
        assert command != null : "command shouldn't be null";

        // call the success callback
        command.onSuccess();

        // launch back the commands execution loop if needed
        if (scheduler != null) {
            scheduler.maybeLaunchCommandsAfterAsyncCommand();
        }
    }

    /**
     * Called back when a load part command failed. Remove all depending commands, calling their fail callback, and restart the command execution loop.
     * 
     * @param command
     *            the load part command that failed. Shouldn't be null.
     * @param scheduler
     *            the scheduler to call back. Can be null. If null, the real scheduler won't be restarted.
     */
    public static void onFailLoadingPart(final OPCommand command, final OPScheduler scheduler) {
        assert command != null : "command shouldn't be null";
        
        // call the failure callback
        command.onFail(OPCommandFailureReason.loadTimeoutFailureFactory(command.getService()));

        if (scheduler != null) {
            // if the command is service dependent
            if (command.getService() != null) {
                // remove all depending commands from the command execution queue
                final OPCommand[] dependingCommands = scheduler.removeAllDependingCommands(command.getService());

                // call fail callback on all removed commands
                for (int i = 0; i < dependingCommands.length; i++) {
                    dependingCommands[i].onFail(OPCommandFailureReason.preCommandFailedFailureFactory(command.getService()));
                }
            }
            
            // launch back the commands execution loop if needed
           scheduler.maybeLaunchCommandsAfterAsyncCommand();            
        }
    }
    
}
